/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Στο παρόν module περιέχονται δομές και μέθοδοι που αφορούν στα παιγνιόχαρτα, στις
// χαρτωσιές, στην τράπουλα κλπ, και είναι κοινές στον server και στους clients.
// Οι τράπουλες θεωρούνται των 32 φύλλων, αποτελούμενες από 7, 8, 9, 10, J, Q, K, A.

// Τα χρώματα των φύλλων είναι:
//
//	S	Μπαστούνια (από το "spades")
//	C	Σπαθιά (από το "clubs")
//	D	Καρά (από το "diamonds")
//	H	Κούπες (από το "hearts")
//
// Σε περιπτώσεις δηλώσεων και αγορών που θα χρειαστεί να αναφερθούμε σε αχρωμάτιστα,
// χρησιμοποιείται το λατινικό "N" από τη λέξη "neutral". Έτσι μια αγορά απλά σπαθιά
// θα συμβολίζεται με "C6", ενώ τα απλά αχρωμάτιστα θα συμβολίζονται "N6".

// Οι αξίες των φύλλων είναι:
//
//	7	Επτάρι
//	8	Οκτάρι
//	9	Εννιάρι
//	T	Δεκάρι (από το "ten")
//	J	Βαλές (από το "jack")
//	Q	Ντάμα (από το "queen")
//	K	Ρήγας (από το "king")
//	A	Άσος (από το "ace")

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η κλάση "Filo" παριστά ένα φύλλο της τράπουλας και φέρει ως properties το χρώμα
// και την αξία του φύλλου, τα οποία περνάμε ως παραμέτρους στην function δημιουργίας
// νέου instance φύλλου, π.χ.
//
//	var f = Filo("H", "K");		// για τον ρήγα κούπα
//	var f = Filo("D", "T");		// για το δέκα καρό

Filo = function(xroma, axia) {
	if (!Trapoula.amorx.hasOwnProperty(xroma)) globals.fatal(xroma + ': απαράδεκτο χρώμα φύλλου');
	if (!Trapoula.aixa.hasOwnProperty(axia)) globals.fatal(axia + ': απαράδεκτη αξία φύλλου');

	this.xroma = xroma;
	this.axia = axia;
}

// Η μέθοδος "sigrisi" χρησιμοποιείται για την ταξινόμηση των φύλλων κατά χρώμα και
// αξία. Η σειρά των χρωμάτων εδώ καθορίζεται από το αντικείμενο "amorx" και είναι:
// μπαστούνια, καρά, σπαθιά και κούπες, ώστε να έχουμε εναλλαγή χρωμάτων όταν υπάρχουν
// φύλλα και από τα τέσσερα χρώματα.
//
// Η μέθοδος εφαρμόζεται σε κάποιο φύλλο (A) το οποίο συγκρίνεται με άλλο φύλλο το
// οποίο περνάμε ως παράμετρο (B). Αν το A είναι «μικρότερο» από το B επιστρέφει -1,
// αν το A είναι «ίδιο» με το B επιστρέφει 0, ενώ αν το A είναι «μεγαλύτερο» από το
// B επιστρέφει 1.

Filo.prototype.sigrisi = function(filo) {
	if (Trapoula.amorx[this.xroma] < Trapoula.amorx[filo.xroma]) return -1;
	if (Trapoula.amorx[this.xroma] > Trapoula.amorx[filo.xroma]) return 1;
	if (Trapoula.aixa[this.axia] < Trapoula.aixa[filo.axia]) return -1;
	if (Trapoula.aixa[this.axia] > Trapoula.aixa[filo.axia]) return 1;
	return 0;
}

// Η μέθοδος "toString" επιστρέφει το φύλλο ως string της μορφής "XA", όπου "X" το χρώμα
// και "A" η αξία του φύλλου, π.χ. για τον ρήγα καρό θα έχω "DK", για το επτάρι σπαθί θα
// έχω "C7", ενώ για το δεκάρι κούπα θα έχω "HT".

Filo.prototype.toString = function() {
	return this.xroma + this.axia;
}

// Η μέθοδος "toFilo" εφαρμόζεται σε ένα string και θεωρώντας τούς δύο πρώτους χαρακτήρες
// ως χρώμα και αξία φύλλου, επιστρέφει το αντίστοιχο φύλλο, π.χ. για το string "S8" θα
// επιστραφεί το οκτάρι μπαστούνι, ενώ για το string "CTSKHQ" θα επιστραφεί το δεκάρι σπαθί.

String.prototype.toFilo = function() {
	if (this.length < 2) globals.fatal(this + ': cannot convert to "Filo"');
	return new Filo(this.substr(0, 1), this.substr(1, 1));
}

// Η μέθοδος "xromaReal" επιστρέφει το πραγματικό χρώμα του φύλλου, δηλαδή
// μαύρο για μπαστούνια και σπαθιά, και κόκκινο για καρά και κούπες. Για
// κρυφά φύλλα επιστρέφει null.

Filo.prototype.xromaReal = function() {
	switch (this.xroma) {
	case 'S':
	case 'C':
		return 'ΜΑΥΡΟ';
	case 'D':
	case 'H':
		return 'ΚΟΚΚΙΝΟ';
	}

	return null;
}

Filo.prototype.isKlisto = function() {
	return !this.xromaReal();
}

Filo.prototype.oxiKlisto = function() {
	return !this.isKlisto();
}

// Η μέθοδος "omioxromo" δέχεται ένα φύλλο και επιστρέφει true αν το ανά
// χείρας φύλλο είναι ομοιόχρωμο με αυτό, αλλιώς επιστρέφει false.

Filo.prototype.omioxromo = function(filo) {
	var x1, x2;

	x1 = this.xromaReal();
	if (!x1) return false;

	x2 = filo.xromaReal();
	if (!x2) return false;

	return(x1 == x2);
}

Filo.prototype.print = function() {
	console.log(this);
	return this;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η κλάση "Xartosia" παριστά μια διατεταγμένη ομάδα χαρτιών την οποία περνάμε ως
// array φύλλων κατά τη δημιουργία νέων αντικειμένων χαρτωσιάς, π.χ. τα φύλλα άσος
// μπαστούνι, ρήγας σπαθί, επτά καρό, οκτώ καρό και άσος κούπα μετατρέπονται σε
// ενιαία χαρτωσιά ως εξής:
//
//	var x = new Xartosia([
//		new Filo("S", "A"),
//		new Filo("C", "K"),
//		new Filo("D", "7"),
//		new Filo("D", "8"),
//		new Filo("H", "A"),
//	]);

Xartosia = function(fila) {
	this.fila = fila ? fila : [];
	this.alifMake();
}

// Το property "alif" είναι λίστα δεικτοδοτημένη με τα φύλλα τής χαρτωσιάς ως
// strings και τιμές τα αντίστοιχα indices στο array των φύλλων της χαρτωσιάς.
// Το συγκεκριμένο property επιταχύνει διάφορες διαδικασίες της χαρτωσιάς και
// μπορεί να επαναδημιουργηθεί από το property "fila" με την μέθοδο "alifMake".

Xartosia.prototype.alifMake = function() {
	var i;

	this.alif = {};
	for (i = 0; i < this.fila.length; i++) {
		this.alif[this.fila[i].toString()] = i;
	}

	return this;
}

// Η μέθοδος "add" προσθέτει νέο φύλλο στη χαρτωσιά.

Xartosia.prototype.add = function(filo) {
	this.alif[filo.toString()] = this.fila.length;
	this.fila.push(filo);
	return this;
}

// Η μέθοδος "addXartosia" προσθέτει τα φύλλα μιας άλλης χαρτωσιάς
// στην ανά χείρας χαρτωσιά και επιστρέφει τη νέα χαρτωσιά.

Xartosia.prototype.addXartosia = function(xartosia) {
	var i;

	for (i = 0; i < xartosia.fila.length; i++) {
		this.add(xartosia.fila[i]);
	}

	return this;
}

// Η μέθοδος "deli" διαγράφει ένα φύλλο από τη χαρτωσιά. Το φύλλο καθορίζεται
// ως αριθμητικό index τού προς διαγραφήν φύλλου στη χαρτωσιά.

Xartosia.prototype.deli = function(i) {
	var filo;

	try {
		filo = this.fila[i];
		this.fila.splice(i, 1);
		this.alifMake();
	} catch (e) {
		globals.fatal('Xartosia.deli: ' + i + ': invalid index');
	}

	return this;
}

// Η μέθοδος "delf" διαγράφει ένα φύλλο από τη χαρτωσιά. Το φύλλο καθορίζεται
// ως αντικείμενο φύλλου.

Xartosia.prototype.delf = function(f) {
	var s;

	try {
		s = f.toString();
	} catch (e) {
		globals.fatal('Xartosia.delf: invalid argument');
	}

	return this.alif.hasOwnProperty(s) ? this.deli(this.alif[s]) : this;
}

// Η μέθοδος "taxinomisi" ταξινομεί μια χαρτωσιά κατά χρώμα και αξία σύμφωνα με
// τη σειρά των χρωμάτων που ορίζεται στο αντικείμενο "xroma".

Xartosia.prototype.taxinomisi = function() {
	this.fila.sort(function(f1, f2) {
		return f1.sigrisi(f2);
	});

	this.alifMake();

	return this;
}

// Η μέθοδος "toString" επιστρέφει την χαρτωσιά ως string με κάθε φύλλο τής
// χαρτωσιάς να μετατρέπεται σε string τής μορφής "XA" όπου "X" το χρώμα και
// "A" η αξία τού φύλλου.

Xartosia.prototype.toString = function() {
	var i, s = '';

	for (i = 0; i < this.fila.length; i++) {
		s += this.fila[i].toString();
	}

	return s;
}

// Η μέθοδος "toXartosia" δέχεται ένα string της μορφής "XAXAXAXA...", όπου
// "X" χρώμα και "A" αξία φύλλου και επιστρέφει την αντίστοιχη χαρτωσιά.

String.prototype.toXartosia = function() {
	var i, x = new Xartosia();

	for (i = 0; i < this.length; i += 2) {
		x.add(this.substr(i).toFilo());
	}

	return x;
}

Xartosia.prototype.print = function() {
	var i;

	for (i = 0; i < this.fila.length; i++) {
		this.fila[i].print();
	}

	return this;
}

// Η μέθοδος "isAsoi" ελέγχει αν η χαρτωσιά περιλαμβάνει τους τέσσερις
// άσους.

Xartosia.prototype.isAsoi = function() {
	var i, asoi = 0;

	for (i in this.fila) {
		if (this.fila[i].axia == 'A') asoi++;
	}

return true;
	return (asoi == 4);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η κλάση "Trapoula" παριστά μια ολόκληρη τράπουλα 32 φύλλων. Τα φύλλα
// τής τράπουλας ενυπάρχουν στο property "deck" που είναι array και αρχικά
// είναι ταξινομημένα σύμφωνα με τη σειρά των χρωμάτων που καθορίζεται από
// το array "Trapoula.xroma".

Trapoula = function() {
	var i, j;

	this.deck = [];
	for (i = 0; i < Trapoula.xroma.length; i++) {
		for (j = 0; j < Trapoula.axia.length; j++) {
			this.deck.push(new Filo(Trapoula.xroma[i], Trapoula.axia[j]));
		}
	}
}

// Η μέθοδος "anakatema" ανακατεύει μια τράπουλα εκτελώντας διαδοχικές εναλλαγές
// τυχαίων φύλλων τής τράπουλας. By default εφαρμόζονται 1.000 εναλλαγές, αλλά
// μπορούμε να περάσουμε ως παράμετρο το επιθυμητό πλήθος εναλλαγών.

Trapoula.prototype.anakatema = function(cnt) {
	var
	n = this.deck.length - 1,
	i, j, k, x, a;

	if (cnt === undefined) cnt = 1000;
	for (i = 0; i < cnt; i++) {
		j = globals.random(0, n);
		k = globals.random(0, n);
		if (j == k) continue;

		x = this.deck[j].xroma;
		a = this.deck[j].axia;

		this.deck[j].xroma = this.deck[k].xroma;
		this.deck[j].axia = this.deck[k].axia;

		this.deck[k].xroma = x;
		this.deck[k].axia = a;
	}

	return this;
}

Trapoula.prototype.xartosia = function(apo, count) {
	var i, x = new Xartosia();

	if (apo === undefined) apo = 0;
	if (count === undefined) count = this.deck.length - apo;

	while (count-- > 0) {
		x.add(new Filo(this.deck[apo].xroma, this.deck[apo].axia));
		apo++;
	}

	return x;
}

Trapoula.prototype.print = function() {
	var i;

	for (i = 0; i < this.deck.length; i++) {
		this.deck[i].print();
	}

	return this;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Trapoula.xroma = [
	'S',	// μπαστούνια
	'D',	// καρά
	'C',	// σπαθιά
	'H',	// κούπες
];
Trapoula.amorx = {};

Trapoula.axia = [
	'7',	// επτάρι
	'8',	// οκτάρι
	'9',	// εννιάρι
	'T',	// δεκάρι
	'J',	// βαλές
	'Q',	// ντάμα
	'K',	// ρήγας
	'A',	// άσος
];
Trapoula.aixa = {};

(function() {
	var i;

	for (i = 0; i < Trapoula.xroma.length; i++) Trapoula.amorx[Trapoula.xroma[i]] = i;
	Trapoula.amorx['B'] = i++;	// κρυφό μπλε
	Trapoula.amorx['R'] = i++;	// κρυφό κόκκινο

	for (i = 0; i < Trapoula.axia.length; i++) Trapoula.aixa[Trapoula.axia[i]] = i;
	Trapoula.aixa['V'] = i++;	// κρυφό όρθιο
})();
